import { RegisterComponent } from "@feng3d/ecs";
import { Material, Mesh } from "../../thirdparty/three/imports";
import { Camera3D } from "../cameras/Camera3D";
import { BufferGeometry3D } from "../core/BufferGeometry3D";
import { Component3D } from "../core/Component3D";
import { Node3D } from "../core/Node3D";
import { Material3D } from "../materials/Material3D";
import { WebGLRenderer3D } from "../renderers/WebGLRenderer3D";
import { Scene3D } from "../scenes/Scene3D";

declare module "@feng3d/ecs"
{
    interface ComponentMap { Mesh3D: Mesh3D; }
}

/**
 * 包装`three`中`Mesh`
 */
@RegisterComponent({ name: 'Mesh3D' })
export class Mesh3D extends Component3D
{
    _mesh: Mesh;

    get geometry()
    {
        if (this._mesh)
        {
            this._geometry = BufferGeometry3D.get(this._mesh.geometry);
        }

        return this._geometry;
    }
    set geometry(v)
    {
        this._geometry = v;
        if (this._mesh)
        {
            this._mesh.geometry = v._geometry;
        }
    }
    private _geometry: BufferGeometry3D;

    get material()
    {
        if (this._mesh)
        {
            this._material = Material3D.get(this._mesh.material as Material);
        }

        return this._material;
    }
    set material(v)
    {
        this._material = v;
        if (this._mesh)
        {
            this._mesh.material = v._material;
        }
    }
    private _material: Material3D;

    get materials()
    {
        if (this._mesh)
        {
            const materials = this._mesh.material as Material[];
            this._materials = materials.map((v) => Material3D.get(v));
        }

        return this._materials;
    }
    set materials(v)
    {
        this._materials = v;
        if (this._mesh)
        {
            this._mesh.material = v.map((v) => v._material);
        }
    }
    private _materials: Material3D[];

    get receiveShadow()
    {
        return this._mesh.receiveShadow;
    }
    set receiveShadow(v)
    {
        this._mesh.receiveShadow = v;
    }

    get castShadow()
    {
        return this._mesh.castShadow;
    }
    set castShadow(v)
    {
        this._mesh.castShadow = v;
    }

    get matrixAutoUpdate()
    {
        return this._mesh.matrixAutoUpdate;
    }
    set matrixAutoUpdate(v)
    {
        this._mesh.matrixAutoUpdate = v;
    }

    get renderOrder()
    {
        return this._mesh.renderOrder;
    }
    set renderOrder(v)
    {
        this._mesh.renderOrder = v;
    }

    init()
    {
        super.init();

        if (!this._mesh)
        {
            if (this._materials)
            {
                const materials = this._materials.map((v) => v._material);
                this._mesh = new Mesh(this.geometry?._geometry, materials);
            }
            else
            {
                this._mesh = new Mesh(this.geometry?._geometry, this.material?._material);
            }
        }

        this._entity._group.add(this._mesh);
        this._entity.isMesh = true;
    }

    destroy(): void
    {
        this._entity._group.remove(this._mesh);
        this._mesh = null;
        this._entity.isMesh = false;

        super.destroy();
    }

    onAfterRender(renderer: WebGLRenderer3D, scene: Scene3D, camera: Camera3D, geometry: BufferGeometry3D, material: Material3D, group: Node3D)
    {
        this._mesh.onAfterRender(renderer._webGLRenderer, scene._scene, camera._camera, geometry._geometry, material._material, group._group);
    }

    _set(mesh: Mesh)
    {
        if (this._mesh)
        {
            this._entity._group.remove(this._mesh);
        }
        this._mesh = mesh;
        if (this._mesh)
        {
            this._entity._group.add(this._mesh);
        }
    }

    static get(mesh: Mesh)
    {
        let mesh3D = this._map.get(mesh);
        if (!mesh3D)
        {
            mesh3D = new Node3D().addComponent("Mesh3D");
            //
            mesh3D.entity.position = mesh.position;
            mesh3D.entity.scale = mesh.scale;
            mesh3D.entity.rotation = mesh.rotation;
            //
            mesh.position.set(0, 0, 0);
            mesh.rotation.set(0, 0, 0);
            mesh.scale.set(1, 1, 1);
            //
            mesh3D._set(mesh);
            //
            this._map.set(mesh, mesh3D);
        }

        return mesh3D;
    }
    protected static _map = new Map<Mesh, Mesh3D>();
}
